#include <QtCore/QProcess>
#include <QtCore/QByteArray>
#include <iostream>
#include <assert.h>
using namespace std;

int tests = 0;
bool firstMake  = true;

bool isWrong(const char* s, const bool expected){
    QString error = "error at line:";
    QString mError = "make: ***";
    QString sFaultMsg = "Segmentation fault";
    QProcess gzip;
    if(firstMake){
        gzip.start("bash", QStringList() << "-c"<<"cd ..;make;./ssCompiler.out");
        firstMake = false;
    } else gzip.start("bash", QStringList() << "-c"<<"cd ..;./ssCompiler.out");
    assert(gzip.waitForStarted());

    assert(gzip.write(s));
    gzip.closeWriteChannel();
    assert(gzip.waitForFinished());

    QString rr = gzip.readAll().data();
    QByteArray result = gzip.readAllStandardError();
    QString r = result.data();
    cerr << r.toStdString() <<endl;
    const bool wrong =  r.contains(error);
    const bool mkError = r.contains(mError);
    const bool sFault = r.contains(sFaultMsg);
    if((wrong != expected) || sFault  || mkError){
        cerr << "*********** failing test: ************"<<endl;
        cerr << s <<endl;
        cerr << rr.toStdString() << endl;
    }
    assert(!mkError && !sFault);
    return wrong;
}

void test(const char* a, const bool expected, const char* msg){
    tests++;
    assert(expected != isWrong(a, !expected));
    cerr << "test passed: " << msg << "\t"<< a << " is " << (expected? "right":"wrong") <<endl;
}

int main(){
    cout << "************* start tests *******************"<<endl;

    char* a = "int main(){}";
    test(a, false,"main with no args and no return");

    a = "int main(){ return 0;}";
    test(a, true, "main with no args returning int value");

    a = "void main(){}";
    test(a, true, "void main with no args");

    a = "int main(int argc, string argv){}";
    test(a, false, "main with (int,string)");

    a = "int main(int argc, string argv){return 0;}";
    test(a, true, "main with (int,string)");

    a = "void main(){} int main(){}";
    test(a, false,"one void and one int main");

    a = "int main(){} int main(){}";
    test(a, false, "two mains");

    a = "float main(){}";
    test(a,false,"float main");

    a = "void main(int a, int b){}";
    test(a, false, "main with 2 distinct args");

    a = "int main(int a, int b, int c){}";
    test(a, false, "main with 3 distinct args");

    a = "int main(int a, int a){}";
    test(a, false,"main with 2 same args");

    a = "void main(int a, int b, int c){}";
    test(a,false,"void main with 3 distinct args");

    a = "void main(int a){}";
    test(a, false,"main with single arg");

    a = "int main1(int a){}";
    test(a, false, "int main1 with a variable and no return");

    a= "void main1(int a){}";
    test(a, true, "void main1 with a variable");

    a = "int foo(){} int foo(){}";
    test(a, false,"2 foos with no return");

    a = "int foo(){}";
    test(a, false,"int foo with no return");

    a = "int foo(){return 0;}";
    test(a, true, "int foo with return");

    a = "int foo(){return 0;} int foo(){return 1;}";
    test(a, false, "two foos");

    a = "void foo(int a, int b){}";
    test(a, true, "void foo with 2 distinct args");

    a = "void foo(int a, float b){int c; int d; int e;}";
    test(a, true, "void function with 2 distinct args, and 3 distinct vars");

    a = "void foo(){int c; float c;}";
    test(a, false, "function with no args, and 2 same vars of different type");

    a = "void foo(int a, float b){int c; int d; int e; int d;}";
    test(a,false,"function with 2 distinct args, and 2 same vars:" );

    a = "int foo(int a, float a){return 0}";
    test(a, false, "function with 2 same name args");

    a = "void main(int a, string b){ int e, f, g;  int h, i;}";
    test(a, true, "void main with 2 distinct args and n distinct variables (extended list declaration):");

    a = "void main(int a, string b){ int e, f, g;  int h, i, e;}";
    test(a, false, "void main with 2 distinct args and 2 same variables (extended list declaration):");

    a = "void main(int a, string b){ int e, f, g, e;}";
    test(a, false, "void main with 2 distinct args and 2 same variables in extended list declaration:");

    a = "void main(int a, string b){ int e, f, g, a;}";
    test(a, false, "void main with 2 distinct args and 2 same variables 1 from args:");

    a = " void main(int a, string b){int c; c = 5;}";
    test(a, true, "void main with 2 distinct args, 1 internal var declaration followed by 1: assign:");

    a = " void main(int a, string b){int d = 5;}";
    test(a, true, "void main with 2 distinct args and 1 inline initialization:");

    a = " void main(int a, string b){float d = 5.0, c = 4.0;}";
    test(a, true, "void main with 2 distinct args and 1 inline initialization:");

    a = " void foo(int b){b = 5;}";
    test(a, true, "void foo one args and one assignment using that variable:");

    a = " void foo(){int a, b, c; b = 5; }";
    test(a, true, "void foo no args and one assignment after a initialization list:");

    a = " int foo(){a = 5;}";
    test(a, false, "int foo one args and one assignment using that variable:");

    a = " int foo(int a, int b, int c){int d, e; int f; g = 5;}";
    test(a, false, "int foo 3 distinct args and 3 variable declaration and one variable assignment:");

    a= "int main(int argc, string argv){int e, f, g; int h; return 0;}";
    test(a, true, "int main with args, and vars and return:");

    a = "void main(int a, string b){float c = 5.0; int d = 4; return;}";
    test(a, true, "void main with args and vars and empty return:");

    a =  "void foo(){return; return; int a; return; int b=5; return;}";
    test(a, true, "void foo with multiple empty returns and some vars:");

    a =  "void foo(string a){int b = \"test\";}";
    test(a, false, "void foo with 1 arg and wrong assignment:");

    a =  "void foo(string b){b = 1;}";
    test(a, false, "void foo with 1 arg and not compatible string-int assignment:");

    a =  "void foo(string b){b = \"test2\";}";
    test(a, true, "void foo with 1 arg and not compatible assignment:");

    a =  "void foo(){int a = 1, c = \"test\";}";
    test(a, false, "void foo with 0 args and not type error in initialization list:");

    a = "int foo(){int b; return b;};";
    test(a,true,"declare a variable and return it:");

    a = "void foo(){int b; return b;}";
    test(a, false, "declare a variable and return it in void function:");

    a = "bool foo(bool a, bool b){return b;};";
    test(a,true,"return a bool arg:");

    a = "void foo(string b){b = 1+3;}";
    test(a,false,"1 arg and 1 assignment of integer sum to string:");

    a = "void foo(){1+\"string\";}";
    test(a,false,"sum of integer with string:");

    a = "void foo(){1+90;}";
    test(a,true,"sum of 2 integers:");

    a = "void foo(){7.0+2.4;}";
    test(a,true,"sum of 2 floats:");

    a = "void foo(){float a = (7.0+2.4) + 4.2 - 3.0;}";
    test(a,true,"mixed operation with parenthesis, addition and subtraction:");

    a = "void foo(){float a = 1.0 / 0.0;}";
    test(a,false,"division by zero constant float:");

    a = "void foo(){float a = 0.0; b = 5.0 / a;}";
    test(a,false,"division by a float variable that holds value zero:");

    a = "void foo(){float a = 5 / (4 - (16/4));}";
    test(a,false,"division by a operation that results in zero:");

    a = "void foo(){float a, b; float c = a / b;}";
    test(a,true,"division of 2 uninitialized float variables:");

    a = "void foo(){int a = 5 % 3;}";
    test(a,true,"modulo operations with ints:");

    a = "void foo(){int a = 5 + -3;}";
    test(a,true,"sum of 2 integers, one of which is negated:");

    a = "void foo(){float a = 5;}";
    test(a,true,"single cast from integer value to float:");

    a = "void foo(){float a; a = 5.0; a = 3;}";
    test(a,true,"overriding of float value with (casted) integer value:");

    a = "float foo(){return 3;}";
    test(a,true,"float foo, but return casted int:");

    a = "void foo(){float t = 5+5.0-3;}";
    test(a,true,"mixed operations with integers and floats (assignment to float var):");

    a = "void foo(){int a = 5+5.0-3;}";
    test(a,false,"mixed operations with integers and floats (assignment to int var):");

    a = "void foo(){int a = 3; float b; b = a;}";
    test(a,true,"assignment of int to float in a different line:");

    a = "void foo(){float b; b = -5.2E-2;}";
    test(a,true,"assignment of a negative e notation expression to a float:");

    a = "bool foo(){bool a = 5 == 5; return a;}";
    test(a, true, "bool assignment:");

    a = "bool foo(){return 5==5;}";
    test(a, true, "return comparison as bool:");

    a = "string foo(){bool a = 5 == 5; return a;}";
    test(a, false, "return a bool as a string:");

    a = "int foo(){return 5==5;}";
    test(a,false, "return bool as int:");

    a = "bool foo(){return (5==5);}";
    test(a,true, "return bool in brackets:");

    a = "bool foo(){return 5==5 && (6==6);}";
    test(a,true, "return bool resulting from and:");

    a = "bool foo(){return 5==5 || 6==6;}";
    test(a,true, "return bool resulting from or:");

    a = "bool foo(){return !5==6 || !(5==6);}";
    test(a, true,"return bool resulting from !:");

    a = "bool foo(){return 5!=6;}";
    test(a,true, "return bool resulting from != comparison:");

    a = "int foo(){return 5!=6;}";
    test(a, false, "return bool resulting from != comparison:");

    a = "bool foo(){return 5>6;}";
    test(a, true, "return bool resulting from 5>6 comparison:");

    a = "bool foo(){return 5<6;}";
    test(a, true, "return bool resulting from 5<6 comparison:");

    a = "bool foo(){return 5.4<=4.5;}";
    test(a, true, "return bool resulting from 5.4<4.5 comparison:");

    a = "bool foo(){return 'a'>='b';}";
    test(a, true, "return bool resulting from 'a'<'b' comparison:");

    a = "bool foo(){return true == false;}";
    test(a, true, "return bool resulting from true == false :");

    a = "bool foo(){return false;}";
    test(a, true, "return boolean false:");

    a = "bool foo(){return \"a\"<\"b\";}";
    test(a, true, "return bool resulting from \"a\"<\"b\" comparison as float:");

    a = "void main(){if(0==0) a =3;}";
    test(a, false, "if condition with wrong variable assignment:");

    a = "void foo(){if (1==0) {if (5==1) int a = 3;}}";
    test(a, true, "nested ifs with 1 large stmt block and one single instruction:");

    a = "int foo(){return foo1();}";
    test(a, false, "return of method call of undefined function");

    a = "int foo(){foo1(); return 0;}";
    test(a,false, "method call of undefined function");

    a = "void foo(){foo1();} void foo1(){}";
    test(a,false, "method call of yet undefined function");

//    a = "int foo(){return foo();}";
//    test(a, true, "recursive method call without parameters");

    a = "int foo(){return 0;} int foo1(){foo(); return 0;}";
    test(a,true,"method call before return");

    a = "void foo(){} void foo1(){} int foo2(){foo(); foo1(); return 0; foo();}";
    test(a,true,"multiple method calls");

    a = "void foo(int a){} void foo1(){foo(5);}";
    test(a, true, "method call with parameter");

    a = "int foo(int a){return 0;} void foo1(){string b; foo(b);}";
    test(a,false,"method call with incorrect parameter");

    a = "int foo(int a){return 0;} int foo1(string a){foo(\"some string\");}";
    test(a,false, "passing string value to function expecting an int");

    a = "int foo(int a,string b){return 0;} int foo1(string a){foo(\"some string\", 5);}";
    test(a,false, "passing parameters of same type but incorrect order");

    a = "int foo(int a, string b, int c){return 0;} int bar(string a){int d; foo(5,a,d); return d;}";
    test(a, true,"calling a function with multiple parameters: 1 value, 1 arg, and 1 var");

    a = "void foo1(int a, int b){} void foo2(int a,int b){foo1(a,b);} void bar(){foo1(5,5); foo2(6,6);}";
    test(a, true, "calling multiple functions");

    a = "void foo(int a, int if){}";
    test(a, false, "using reserved keyword as argument name");

    a = "void foo(int a, int b){int int;}";
    test(a, false, "using reserved keywords as variable identifiers");

    cout << "************* all "<< tests <<" tests passed *******************"<<endl;
    return 0;
}
